package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.naming.Context;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @Author: Tao Zheng
 * @classNmae: Bootstrap
 * @packageName: server
 * @description:Minicat主类
 * @DATE: 2021/7/13 11:45
 */
public class Bootstrap {

    /**
     * 定义socket监听的端口号
     * @param args
     */
    private int port = 8080;

    private MyMapper myMapper = new MyMapper();

    //项目路径
    private String webappsPath="";

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public MyMapper getMyMapper() {
        return myMapper;
    }

    public void setMyMapper(MyMapper myMapper) {
        this.myMapper = myMapper;
    }

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws IOException {
        // 3.0 加载解析web.xml
//        loadServlet();

        // 4.0 通过server.xml配置文件读取所有配置信息
        loadServer();


        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize =50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("========>>>> Minicat start on port:" + port);


        // 完成Minicat 1.0版本（浏览器请求http://localhost：8080,返回一个固定的字符串到页面"Hello Minicat!"
        // while (true){
        //     Socket socket = serverSocket.accept();
        //     OutputStream outputStream = socket.getOutputStream();
        //     String data = "Hello Minicat";
        //     String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length) + data;
        //     outputStream.write(responseText.getBytes());
        //     socket.close();
        // }


        // /**
        //  * 完成Minicat 2.0 版本
        //  * 需求：封装Request和Response对象，返回html静态资源文件
        //  */
        // while (true){
        //     Socket socket = serverSocket.accept();
        //     InputStream inputStream = socket.getInputStream();
        //
        //     //封装Request对象和Response对象
        //     Request request = new Request(inputStream);
        //     Response response = new Response(socket.getOutputStream());
        //     response.outputHtml(request.getUrl());
        //     socket.close();
        // }

        /**
         * 完成Minicat 3.0 版本
         * 需求：可以请求动态资源
         */
//        while (true){
//            Socket socket = serverSocket.accept();
//            InputStream inputStream = socket.getInputStream();
//
//            //封装Request对象和Response对象
//            Request request = new Request(inputStream);
//            Response response = new Response(socket.getOutputStream());
//            response.outputHtml(request.getUrl());
//            socket.close();
//        }

        /**
         *
         * 多线程改造（不使用线程池）
         *
         */
//        while(true) {
//            Socket socket = serverSocket.accept();
//            RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
//            requestProcessor.start();
//        }


        /*
            多线程改造（使用线程池）
         */
        while(true) {

            Socket socket = serverSocket.accept();
            RequestProcessorV requestProcessorv = new RequestProcessorV(socket,myMapper,webappsPath);
            //requestProcessor.start();
            threadPoolExecutor.execute(requestProcessorv);
        }
    }

    private Map<String,HttpServlet> servletMap = new HashMap<String,HttpServlet>();

    private void loadServer(){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();

        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            // 获取server.xml配置文件中的所有host标签信息
            List<Element> hostNodes = rootElement.selectNodes("//host");
            for (int i = 0; i < hostNodes.size(); i++) {

                Element hostElement = hostNodes.get(i);




                MyHost myHost = new MyHost();


                String hostName = hostElement.attributeValue("name");
                String appBase = hostElement.attributeValue("appBase");

                // 获取项目集绝对路径
                webappsPath = appBase.replaceAll("\\\\","/");

                File allFile = new File(appBase);
                // 对webapps文件夹路径进行判定，是否存在
                if(allFile.exists() && !allFile.isFile()) {
                    File[] files = allFile.listFiles();
                    for (File file : files) {
                        MyContext context = new MyContext();
                        String webName = file.getName();
                        context.setContextPath(webName);

                        // 获取每个webapp下的web.xml文件
                        File webFile = new File(file, "web.xml");
                        // 改造loadServlet（），读取web.xml文件并返回每个demo中所有的Servlet,这里采用Map方式存储，便于后面查询
                        Map<String, MyWrapper> myWrappers = loadWebXmlInfo(file.getPath(),webFile.getAbsoluteFile());
                        context.setWrapperMap(myWrappers);
                        //  存入
                        myHost.getContextMap().put(webName, context);
                    }
                }
                myHost.setHostName(hostName);
                myMapper.getHostMap().put(hostName,myHost);
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    private Map<String, MyWrapper> loadWebXmlInfo(String webPath, File absoluteFile) {

        Map<String, MyWrapper> myWrappers = new HashMap<>();

        try {
            InputStream xmlFileAsStream = new FileInputStream(absoluteFile);
            SAXReader saxReader = new SAXReader();

            Document document = saxReader.read(xmlFileAsStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {

                Element element =  selectNodes.get(i);
                // <servlet-name>lagou</servlet-name>
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletnameElement.getStringValue();
                // <servlet-class>server.LagouServlet</servlet-class>
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletclassElement.getStringValue();


                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                // /lagou
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                String pakageDir = webPath.replaceAll("\\\\","/");
                MyClassLoader loader = new MyClassLoader(pakageDir+"/");
                Class<?> clazz  = loader.findClass(servletClass);
                // 将url和Servlet信息保存到MyWrapper中，再添加到list中
                MyWrapper myWrapper = new MyWrapper();
                myWrapper.setUrlPattern(urlPattern);
                myWrapper.setServlet((HttpServlet) clazz.getClassLoader().loadClass(servletClass).newInstance());
                myWrappers.put(urlPattern,myWrapper);
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return myWrappers;
    }

    /**
     * 加载解析web.xml，初始化Servlet
     *
     */
//    private void loadServlet(){
//        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
//        SAXReader saxReader = new SAXReader();
//
//        try {
//            Document document = saxReader.read(resourceAsStream);
//            Element rootElement = document.getRootElement();
//
//            List<Element> selectNodes = rootElement.selectNodes("//servlet");
//            for (int i = 0; i < selectNodes.size(); i++) {
//                Element element =  selectNodes.get(i);
//                // <servlet-name>lagou</servlet-name>
//                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
//                String servletName = servletnameElement.getStringValue();
//                // <servlet-class>server.LagouServlet</servlet-class>
//                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
//                String servletClass = servletclassElement.getStringValue();
//
//
//                // 根据servlet-name的值找到url-pattern
//                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
//                // /lagou
//                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
//                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
//
//            }
//
//        } catch (DocumentException e) {
//            e.printStackTrace();
//        } catch (IllegalAccessException e) {
//            e.printStackTrace();
//        } catch (InstantiationException e) {
//            e.printStackTrace();
//        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
//        }
//    }

    /**
     * Minicat程序启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try{
            bootstrap.start();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
